package com.sojson.project.sys.user.service.impl;

import java.io.IOException;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

import javax.annotation.Resource;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.sojson.base.impl.BaseListDto;
import com.sojson.base.impl.BaseUser;
import com.sojson.config.exception.TransErrorCode;
import com.sojson.config.exception.bean.Status;
import com.sojson.config.mybatis.annotation.DataScope;
import com.sojson.constant.Constant;
import com.sojson.constant.ConstantField;
import com.sojson.constant.Lock;
import com.sojson.enums.EEnvironmentUserType;
import com.sojson.enums.EUserType;
import com.sojson.enums.core.EDisableAtType;
import com.sojson.enums.core.EGeneralYesNo;
import com.sojson.enums.core.ERoleType;
import com.sojson.enums.core.EUserStatus;
import com.sojson.project.query.entity.bo.Select;
import com.sojson.project.sys.menu.dao.MenuDao;
import com.sojson.project.sys.role.dao.RoleDao;
import com.sojson.project.sys.role.entity.dto.UserRoleDto;
import com.sojson.project.sys.role.entity.po.UserRole;
import com.sojson.project.sys.role.entity.vo.RoleVo;
import com.sojson.project.sys.role.service.RoleService;
import com.sojson.project.sys.role.service.UserDeptService;
import com.sojson.project.sys.role.service.UserRoleService;
import com.sojson.project.sys.user.dao.UserDao;
import com.sojson.project.sys.user.entity.bo.AuthcUser;
import com.sojson.project.sys.user.entity.dto.UserDto;
import com.sojson.project.sys.user.entity.dto.UserPageDto;
import com.sojson.project.sys.user.entity.po.User;
import com.sojson.project.sys.user.entity.vo.UserVo;
import com.sojson.project.sys.user.service.UserService;
import com.sojson.util.BeanUtil;
import com.sojson.util.DateUtil;
import com.sojson.util.ExpUtil;
import com.sojson.util.StringUtil;
import com.sojson.util.bean.OtherUser;
import com.sojson.util.enums.ELoginOtherType;
import com.sojson.util.file.FileUtil;
import com.sojson.util.file.bean.UploadBean;
import com.sojson.util.file.constant.ConstantFileFieldName;
import com.sojson.util.file.depot.FileDepotUtil;
import com.sojson.util.file.enums.EFileTable;
import com.sojson.util.file.enums.EFileTableBase;
import com.sojson.util.param.ParamUtil;
import com.sojson.util.pay.wx.WxUtil;
import com.sojson.util.pay.wx.bean.WxResult;
import com.sojson.util.pwd.PwdUtil;
import com.sojson.util.token.Token;
import com.sojson.util.token.TokenDto;
import com.sojson.util.token.TokenUtil;
import com.sojson.util.token.interfaces.BaseUserService;
import com.sojson.util.verifycode.VerifyCodeUtil;
import com.sojson.util.verifycode.VerifyCodeUtil.Verify;

/**
 * 用户表Service实现类
 *
 * @author liu
 * @date 2021-03-16
 */
@Service
public class UserServiceImpl extends ServiceImpl<UserDao, User> implements UserService, BaseUserService {

    @Lazy
    @Autowired
    private UserServiceImpl userService;
    @Autowired
    private RoleService roleService;
    @Autowired
    private UserRoleService userRoleService;
    @Autowired
    private UserDeptService userDeptService;
    @Autowired
    private AuthenticationManager authenticationManager;
    @Resource
    private UserDao userDao;
    @Resource
    private RoleDao roleDao;
    @Resource
    private MenuDao menuDao;

    /**
     * 判断用户是否有操作权限
     *
     * @param userIds
     * @return
     * @throws IOException
     */
    @Override
    public boolean isPerm(List<String> userIds) throws IOException {
        if (ParamUtil.isAdminSuper()) {
            return true;
        }

        // 判断是否有查询权限
        // ParamUtil.verifyDept(userAdminService.selectDeptIdById(userIds));
        Set<String> roleIdsExist = TokenUtil.getRoleIdsExist();

        for (String id : userIds) {
            // 获取当前用户的角色ID列表
            Set<String> roleIds = roleService.getRoleIdsByUserId(id);
            boolean isRole = false;
            for (String roleId : roleIds) {
                if (roleIdsExist.contains(roleId)) {
                    isRole = true;
                    break;
                }
            }
            if (!isRole) {
                ExpUtil.throwEx(TransErrorCode.NOT_ROLE_PERMISSION);
            }
        }
        return false;
    }

    /**
     * 修改用户头像
     *
     * @param headImg
     * @return
     * @throws IOException
     */
    @Override
    @Transactional(rollbackFor = Throwable.class)
    public boolean updateHeadImg(String headImg) throws IOException {
        String id = TokenUtil.getUserId();
        // 旧数据
        BaseUser findById = userDao.findById(id);

        // 修改数据
        User dto = new User();
        dto.setId(id);
        dto.setHeadImg(headImg);
        int updateById = userDao.updateById(dto);

        if (updateById > 0) {
            FileDepotUtil.updateSync(EFileTable.USER.getCode(), ConstantFileFieldName.HEAD_IMG, headImg,
                findById.getHeadImg());
        }

        // 更新缓存
        TokenUtil.refreshTokenUser(userDao.findById(TokenUtil.getUserId()));
        return false;
    }

    /**
     * 通过id更新个人信息
     *
     * @param dto
     * @return
     * @throws IOException
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateInfo(BaseListDto dto) throws IOException {
        User user = new User();
        BeanUtil.copyBeanProp(user, dto);
        // 参数初始化
        user.updateProfileInit();

        String id = user.getId();
        UserVo findById = userDao.findById(id);
        if (findById == null) {
            ExpUtil.throwEx("用户不存在!");
        }

        String mobile = user.getMobile();
        List<UserVo> findByMobile = userDao.findByMobile(mobile);
        if (StringUtil.isNotBlankObject(findByMobile)) {
            int size = findByMobile.size();
            if (size > 1) {
                ExpUtil.throwEx("手机号已存在！");
            } else if (size == 1 && !findByMobile.get(0).getId().equals(id)) {
                ExpUtil.throwEx("手机号已存在！");
            }
        }

        // 修改用户
        userDao.updateById(user);

        // 更新缓存
        TokenUtil.refreshTokenUser(userDao.findById(TokenUtil.getUserId()));
        return true;
    }

    /**
     * 通过id验证旧密码修改密码
     *
     * @param dto
     * @return
     * @throws Exception
     */
    @Override
    public boolean updatePwdByOldPwd(AuthcUser dto) throws Exception {
        String id = dto.getId();
        UserVo findById = userDao.findById(id);
        // 查看旧密码是否输入正确
        // 判断密码是否正确
        if (!PwdUtil.matchesPwd(dto.getOldPwd(), findById.getPwd())) {
            ExpUtil.throwEx(TransErrorCode.ERR_OLD_PWD);
        }

        User user = new User();
        user.setId(id);
        user.setPwd(Constant.getUserPassword(dto.getPwd()));
        userDao.updateById(user);
        return true;
    }

    /**
     * 短信注册
     *
     * @param dto
     * @return
     * @throws Exception
     */
    @Override
    public UserVo smsRegister(TokenDto dto) throws Exception {
        UserVo user = (UserVo)dto.getUser();

        // 插入初始化
        dto.setDayGoOn(null);
        user.insertInit(dto);
        user.setUsername(null);
        user.setWxUnionid(null);

        synchronized (Lock.BALANCE_LOG) {
            synchronized (Lock.REGISTER_MOBILE_USERNAME) {
                return userService.smsRegisterGo(dto);
            }
        }
    }

    @Transactional(rollbackFor = Throwable.class)
    public UserVo smsRegisterGo(TokenDto dto) throws Exception {
        UserVo user = (UserVo)dto.getUser();

        // 查看此手机号是否存在
        int countByMobile = userDao.countByMobile(user.getMobile());
        // 如果存在就抛异常
        if (countByMobile > 0) {
            ExpUtil.throwEx(TransErrorCode.EXIST_USER);
        }

        // 处理其他业务
        User registerStart = userService.registerStart(user);

        // 返回结果
        int insert = userDao.insert(user);
        if (insert < 1) {
            ExpUtil.throwEx("注册用户失败！");
        }

        // 处理其他业务
        userService.registerEnd(dto, registerStart);

        return user;
    }

    /**
     * 小程序注册
     *
     * @param dto
     * @return
     * @throws Exception
     */
    @Override
    public UserVo xcxRegister(TokenDto dto) throws Exception {
        UserVo user = (UserVo)dto.getUser();

        // 用户不存在,注册用户
        // 插入初始化
        dto.setDayGoOn(null);
        user.insertInit(dto);
        user.setUsername(null);
        user.setMobile(null);

        synchronized (Lock.BALANCE_LOG) {
            return userService.xcxRegisterGo(dto);
        }
    }

    @Transactional(rollbackFor = Throwable.class)
    public UserVo xcxRegisterGo(TokenDto dto) throws Exception {
        UserVo user = (UserVo)dto.getUser();

        synchronized (Lock.BALANCE_LOG) {
            // 查看此微信号是否存在
            int countByWxUnionid = userDao.countByWxUnionid(user.getWxUnionid());
            // 如果存在就抛异常
            if (countByWxUnionid > 0) {
                ExpUtil.throwEx(TransErrorCode.EXIST_USER);
            }

            // 处理其他业务
            User registerStart = userService.registerStart(user);

            // 返回结果
            int insert = userDao.insert(user);
            if (insert < 1) {
                ExpUtil.throwEx("注册失败！");
            }

            // 处理其他业务
            userService.registerEnd(dto, registerStart);
        }
        return user;
    }

    /**
     * 统一的注册处理(插入前处理)
     *
     * @param user
     * @return
     */
    @Transactional(rollbackFor = Throwable.class)
    public User registerStart(UserVo user) {
        // 设置上级ID
        // 查询上级对象
        String invitationCode = user.getInvitationCode();
        if (StringUtil.isBlankObject(invitationCode)) {
            return null;
        }
        User findByInvitationCode = userDao.findByInvitationCode(invitationCode);
        if (StringUtil.isBlankObject(findByInvitationCode)) {
            return null;
        }
        // 上级ID
        String id = findByInvitationCode.getId();
        // 清空邀请码
        user.setInvitationCode(null);
        if (StringUtil.isNotBlankObject(findByInvitationCode)) {
            user.setTopId(id);
        }
        return findByInvitationCode;
    }

    /**
     * 统一的注册处理(插入后处理)
     *
     * @param dto       当前用户信息
     * @param topMem    上级用户信息
     * @throws Exception
     */
    @Transactional(rollbackFor = Throwable.class)
    public void registerEnd(TokenDto dto, User topMem) throws Exception {
        UserVo user = (UserVo)dto.getUser();
        // 设置邀请码
        user.createInvitationCode();
        int updateById = userDao.updateById(user);
        if (updateById < 1) {
            ExpUtil.throwEx("设置邀请码失败！");
        }
        byte[] wxAcodeunLimit = WxUtil.getWxAcodeunLimit(user.getInvitationCode());
        UploadBean upload = FileUtil.upload(wxAcodeunLimit, "/xcxImg", "." + Constant.GENERATE_IMG_SUFFIX);
        if (StringUtil.isBlankObject(upload.getObjectName())) {
            ExpUtil.throwEx("小程序码生成失败！");
        }

        List<UserRole> arrayList = new ArrayList<>();
        Set<String> roleIds = new HashSet<>();
        roleIds.add(ERoleType.USER.getCode());
        for (String roleId : roleIds) {
            UserRole userRole = new UserRole();
            arrayList.add(userRole);
            userRole.setUserId(user.getId());
            userRole.setRoleId(roleId);
        }
        boolean saveBatch = userRoleService.saveBatch(arrayList);
        if (!saveBatch) {
            ExpUtil.throwEx("添加用户角色失败");
        }

        // if (StringUtil.isNotBlankObject(topMem)) {
        // // 添加佣金记录
        // BalanceLog balanceLog = new BalanceLog();
        // balanceLog.insertInitRegister("用户推广", user, topMem.getBalance());
        // String userId = user.getId() + "";
        // balanceLog.setOrderNo(userId);
        // balanceLog.setMark(userId);
        // balanceLogService.insert(balanceLog);
        //
        // // 修改佣金金额
        // Member mem = new Member();
        // mem.updateInit();
        // mem.setId(topMem.getId());
        // mem.setBalance(topMem.getBalance().add(balanceLog.getTotal()));
        // userDao.updatePartialById(mem);
        // }
    }

    /**
     * 短信登录
     *
     * @param dto
     * @return
     * @throws Exception
     */
    @Override
    public Token smsLogin(TokenDto dto) throws Exception {
        return submitLogin(dto, dto.getUser().getMobile());
    }

    /**
     * 微信小程序登录
     *
     * @param dto
     * @return
     * @throws Exception
     */
    @Override
    public Token wxXcxLogin(TokenDto dto) throws Exception {
        User user = new User();
        BeanUtil.copyBeanProp(user, dto.getUser());
        dto.setUser(user);

        return submitLogin(dto, user.getOpenid());
    }

    /**
    * 登录
    *
    * @param dto
    * @return
    * @throws Exception
    */
    public Token submitLogin(TokenDto dto, String username) throws Exception {
        // 保存参数
        TokenUtil.setLoginParam(dto);

        // 用户验证
        // 该方法会去调用UserDetailsService.loadUserByUsername
        Authentication authenticate = authenticationManager
            .authenticate(new UsernamePasswordAuthenticationToken(username, dto.getUser().getPwd()));
        Token token = (Token)authenticate.getPrincipal();

        return token;
    }

    /**
     * 通过登录信息获取用户
     *
     * @param dto
     * @param username
     * @return
     * @throws Exception
     */
    @Override
    public BaseUser loadUserByUsername(TokenDto dto, String username) throws Exception {
        // 获取参数
        TokenDto param = TokenUtil.getLoginParam();
        Set<String> roleIds = param.getRoleIds();

        // 获取用户
        BaseUser user = null;
        if (ELoginOtherType.MOBILE.getCode() == param.getLoginType()) {
            List<UserVo> findByMobile = userService.findByMobile(username);
            if (StringUtil.isBlankObject(findByMobile) || findByMobile.size() < 1) {
                user = userService.smsRegister(param);
            } else if (findByMobile.size() > 1) {
                ExpUtil.throwEx("此手机号存在多个帐号,请联系管理员进行处理");
            } else {
                user = findByMobile.get(0);
            }

            int countByRoleMobile = userService.countByRoleMobile(roleIds, username);
            if (countByRoleMobile < 1) {
                ExpUtil.throwEx("权限不足");
            }
        } else if (ELoginOtherType.WX.getCode() == param.getLoginType()
            || ELoginOtherType.WX_XCX.getCode() == param.getLoginType()) {
            List<UserVo> findByOpenid = userService.findByOpenid(username);
            if (StringUtil.isBlankObject(findByOpenid) || findByOpenid.size() < 1) {
                user = userService.xcxRegister(param);
            } else if (findByOpenid.size() > 1) {
                ExpUtil.throwEx("此微信号存在多个帐号,请联系管理员进行处理");
            } else {
                user = findByOpenid.get(0);

                // 实时修改用户信息
                BaseUser pam = param.getUser();
                String headImgParam = pam.getHeadImg();
                String nickNameParam = pam.getNickName();
                String headImg = user.getHeadImg();
                String nickName = user.getNickName();
                if (StringUtil.isBlankObject(headImg) || !headImg.equals(headImgParam)
                    || StringUtil.isBlankObject(nickName) || !nickName.equals(nickNameParam)) {
                    User update = new User();
                    update.setId(user.getId());
                    update.setHeadImg(headImgParam);
                    update.setNickName(nickNameParam);
                    userService.updateById(update);
                }
            }

            int countByRoleOpenid = userService.countByRoleOpenid(roleIds, username);
            if (countByRoleOpenid < 1) {
                ExpUtil.throwEx("权限不足");
            }
        }
        return user;
    }

    /**
     * 通过id更新
     *
     * @param dto
     * @return
     * @throws IOException
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean update(UserDto dto) throws IOException {
        User user = new User();
        BeanUtil.copyBeanProp(user, dto);
        // 参数初始化
        user.updateInit();

        String id = user.getId();
        UserVo findById = userDao.findById(id);
        if (findById == null) {
            ExpUtil.throwEx("用户不存在!");
        }

        String mobile = user.getMobile();
        List<UserVo> findByMobile = userDao.findByMobile(mobile);
        if (StringUtil.isNotBlankObject(findByMobile)) {
            int size = findByMobile.size();
            if (size > 1) {
                ExpUtil.throwEx("手机号已存在！");
            } else if (size == 1 && !findByMobile.get(0).getId().equals(id)) {
                ExpUtil.throwEx("手机号已存在！");
            }
        }

        // 超级管理员不能禁用
        List<RoleVo> getRole = roleService.getRolesByUserId(id);

        // 修改用户
        int updateById = userDao.updateById(user);

        Set<String> roleIds = dto.getRoleIds();
        // 将没有权限修改的角色保留
        List<String> listIds = getRole.stream().map(RoleVo::getId).collect(Collectors.toList());
        for (String roleId : listIds) {
            if (!ParamUtil.isRoleIdExist(roleId)) {
                roleIds.add(roleId);
            }
        }
        // 删除原有角色
        UserRoleDto userRoleDto = new UserRoleDto();
        userRoleDto.setUserId(id);
        userRoleService.removeByDto(userRoleDto);

        // 添加角色
        if (roleIds.size() > 0) {
            // 新增角色
            List<UserRole> arrayList = new ArrayList<>();
            for (String roleId : roleIds) {
                UserRole userRole = new UserRole();
                arrayList.add(userRole);
                userRole.setUserId(id);
                userRole.setRoleId(roleId);
            }
            boolean saveBatch = userRoleService.saveBatch(arrayList);
            if (!saveBatch) {
                ExpUtil.throwEx("添加用户角色失败");
            }
        }

        // 判断权限环境是否匹配
        List<String> envs = new ArrayList<>(5);
        EEnvironmentUserType[] values = EEnvironmentUserType.values();
        for (EEnvironmentUserType env : values) {
            if (EUserType.USER.getCode().equals(env.getType())) {
                envs.add(env.getCode());
            }
        }
        int countByNotEnv = roleService.countByNotEnv(envs, roleIds);
        if (countByNotEnv > 0) {
            ExpUtil.throwEx("角色环境不匹配!");
        }

        if (updateById > 0) {
            FileDepotUtil.updateSync(EFileTableBase.USER.getCode(), ConstantFileFieldName.HEAD_IMG, user.getHeadImg(),
                findById.getHeadImg());
            FileDepotUtil.updateSync(EFileTableBase.USER.getCode(), ConstantFileFieldName.ID_CARD_FRONT,
                user.getIdCardFront(), findById.getIdCardFront());
            FileDepotUtil.updateSync(EFileTableBase.USER.getCode(), ConstantFileFieldName.ID_CARD_VERSO,
                user.getIdCardVerso(), findById.getIdCardVerso());
            FileDepotUtil.updateSync(EFileTableBase.USER.getCode(), ConstantFileFieldName.LABOR_CARD,
                user.getLaborCard(), findById.getLaborCard());
            FileDepotUtil.updateSync(EFileTableBase.USER.getCode(), ConstantFileFieldName.FOOD_CARD, user.getFoodCard(),
                findById.getFoodCard());
            FileDepotUtil.updateSync(EFileTableBase.USER.getCode(), ConstantFileFieldName.TELECOM_CARD,
                user.getTelecomCard(), findById.getTelecomCard());
        }

        // 踢出此账号的所有Token
        TokenUtil.logout(findById.getId());
        return true;
    }

    /**
     * 通过对象查找
     *
     * @param dto
     * @return
     */
    @Override
    @DataScope(roleIsAll = true, roleAlias = "ur", roleField = ConstantField.ROLE_ID)
    public List<UserVo> list(UserDto dto) {
        List<UserVo> list = userDao.list(dto);
        return list;
    }

    /**
     * 通过对象分页查找
     *
     * @param dto
     * @return
     */
    @Override
    @DataScope(roleIsAll = true, roleAlias = "ur", roleField = ConstantField.ROLE_ID)
    public List<UserVo> page(UserPageDto dto) {
        List<UserVo> page = userDao.page(dto);
        return page;
    }

    /**
     * 通过id查找
     *
     * @param id
     * @return
     */
    @Override
    public UserVo findById(String id) {
        UserVo findById = userDao.findById(id);
        return findById;
    }

    /**
     * 通过对象删除
     *
     * @param dto
     * @return
     */
    @Override
    public boolean removeByDto(UserDto dto) {
        userDao.removeByDto(dto);
        return true;
    }

    /**
     * 清空数据
     *
     * @return
     */
    @Override
    public boolean clear() {
        userDao.clear();
        return true;
    }

    /**
     * 密码重置
     *
     * @param dto
     * @return
     * @throws IOException
     */
    @Override
    public boolean resetUserPwd(User dto) throws IOException {
        String pwd = dto.getPwd();
        if (StringUtil.isBlankObject(pwd)) {
            pwd = Constant.getUserPassword();
        } else {
            pwd = Constant.getUserPassword(pwd);
        }

        dto.setPwd(pwd);
        userDao.updateById(dto);

        TokenUtil.logout(dto.getId());
        return true;
    }

    /**
     * 通过id列表删除
     *
     * @param ids
     * @return
     * @throws IOException
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean removeByIds(List<String> ids) throws IOException {
        userDao.deleteBatchIds(ids);

        // 删除用户关联的角色
        userRoleService.deleteByUserIds(new HashSet<>(ids));

        // 删除用户关联的部门
        userDeptService.deleteByUserIds(new HashSet<>(ids));

        TokenUtil.logout(ids);
        return true;
    }

    /**
     * 通过id修改状态
     *
     * @param dto
     * @return
     * @throws IOException
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateStatus(User dto) throws IOException {
        userDao.updateById(dto);

        // 如果禁用就踢出此账号的所有Token
        if (dto.getStatus() == EUserStatus.DISABLE.getCode()) {
            UserVo findById = userDao.findById(dto.getId());
            if (StringUtil.isNotBlankObject(findById)) {
                TokenUtil.logout(findById.getId());
            }
        }
        return true;
    }

    /**
     * 统计相同帐号的用户数量(判断帐号是否有用户)
     *
     * @param username 帐号
     * @return
     */
    @Override
    public int countByUsername(String username) {
        return userDao.countByUsername(username);
    }

    /**
     * 通过帐号查询
     *
     * @param username
     * @return
     */
    @Override
    public UserVo findByUsername(String username) {
        return userDao.findByUsername(username);
    }

    /**
     * 统计手机号相同的用户数量(判断手机号是否有用户,一个手机号只能有一个用户)
     *
     * @param mobile 手机号
     * @return
     */
    @Override
    public int countByMobile(String mobile) {
        return userDao.countByMobile(mobile);
    }

    /**
     * 统计手机号相同的用户数量(判断手机号是否有用户,一个手机号只能有一个用户)
     *
     * @param roleIds   角色ID列表
     * @param mobile
     * @return
     */
    @Override
    public int countByRoleMobile(Set<String> roleIds, String mobile) {
        return userDao.countByRoleMobile(roleIds, mobile);
    }

    /**
     * 通过手机号查询
     *
     * @param mobile
     * @return
     */
    @Override
    public List<UserVo> findByMobile(String mobile) {
        return userDao.findByMobile(mobile);
    }

    /**
     * 通过手机号查询
     *
     * @param roleIds   角色列表
     * @param mobile
     * @return
     */
    @Override
    public List<UserVo> findByRoleMobile(List<String> roleIds, String mobile) {
        return userDao.findByRoleMobile(roleIds, mobile);
    }

    /**
     * 通过用户ID查询部门ID
     *
     * @param ids
     * @return
     */
    @Override
    public Set<String> selectDeptIdById(List<String> ids) {
        return userDao.selectDeptIdById(ids);
    }

    /**
     * 移动应用绑定微信
     *
     * @param code 用户授权后的code
     * @return
     * @throws Exception
     */
    @Override
    public boolean wxAppBindOpenid(String code) throws Exception {
        WxResult appAccessToken = WxUtil.getUserAccessToken(code);
        Status status = appAccessToken.getStatus();
        if (status.getCode() != TransErrorCode.SUCCESS.getCode()) {
            ExpUtil.throwEx(status.getMsg());
        }

        User user = new User();
        user.setId(TokenUtil.getUserId());
        user.setOpenid(appAccessToken.getOpenid());
        userDao.updateById(user);
        return true;
    }

    /**
     * 小程序绑定微信
     *
     * @param code 用户授权后的code
     * @return
     * @throws Exception
     */
    @Override
    public boolean wxXcxBindOpenid(String code) throws Exception {
        // TODO
        OtherUser userMessage = WxUtil.getUserMessageXcx(code);
        String userId = TokenUtil.getUserId();
        UserVo findById = userDao.findById(userId);
        if (StringUtil.isNotBlankString(findById.getOpenid())) {
            return true;
        }

        User user = new User();
        user.setId(userId);
        user.setOpenid(userMessage.getOpenid());
        userDao.updateById(user);
        return true;
    }

    /**
     * 通过id更新手机号
     *
     * @param dto
     * @return
     * @throws IOException
     */
    @Override
    public boolean updateMobileById(TokenDto dto) throws IOException {
        BaseUser user = dto.getUser();
        String id = user.getId();
        String mobile = user.getMobile();
        String string = new StringBuilder(TokenUtil.getTokenId()).append(VerifyCodeUtil.SMS)
            .append(dto.getVerifyCodePrefix()).toString();
        Verify verifyCode = VerifyCodeUtil.getVerifyCode(string);

        if (!VerifyCodeUtil.verifyAndClearCode(verifyCode, dto.getVerifyCodeValue())) {
            ExpUtil.throwEx(TransErrorCode.ERR_CODES.getMsg());
        }

        User findById = userDao.findById(id);
        if (StringUtil.isBlankObject(findById)) {
            ExpUtil.throwEx("用户不存在！");
        }
        String m = findById.getMobile();
        if (StringUtil.isNotBlankObject(m) && !m.equals(verifyCode.getUsername())) {
            ExpUtil.throwEx("验证码不是旧手机的！");
        }

        synchronized (Lock.REGISTER_MOBILE_USERNAME) {
            List<UserVo> findByMobile = userDao.findByMobile(mobile);
            if (StringUtil.isNotBlankObject(findByMobile)) {
                int size = findByMobile.size();
                if (size > 1) {
                    ExpUtil.throwEx("手机号已存在！");
                } else if (size == 1 && !findByMobile.get(0).getId().equals(id)) {
                    ExpUtil.throwEx("手机号已存在！");
                }
            }

            User u = new User();
            u.setId(id);
            u.setMobile(mobile);
            userDao.updateById(u);
        }
        return true;
    }

    /**
     * 通过id验证短信修改密码
     *
     * @param token
     * @return
     * @throws Exception
     */
    @Override
    public boolean updatePwdBySms(Token token) throws Exception {
        User user = new User();
        user.setId(token.getUser().getId());
        user.setPwd(Constant.getUserPassword(token.getUser().getPwd()));
        userDao.updateById(user);
        return true;
    }

    /**
     * 统计微信openid相同的用户数量
     *
     * @param openid
     * @return
     */
    @Override
    public int countByOpenid(String openid) {
        return userDao.countByOpenid(openid);
    }

    /**
     * 统计微信openid相同的用户数量
     *
     * @param roleIds   角色列表
     * @param openid
     * @return
     */
    @Override
    public int countByRoleOpenid(Set<String> roleIds, String openid) {
        return userDao.countByRoleOpenid(roleIds, openid);
    }

    /**
     * 通过微信openid查询
     *
     * @param openid
     * @return
     */
    @Override
    public List<UserVo> findByOpenid(String openid) {
        return userDao.findByOpenid(openid);
    }

    /**
     * 通过微信openid查询
     *
     * @param roleIds   角色列表
     * @param openid
     * @return
     */
    @Override
    public List<UserVo> findByRoleOpenid(List<String> roleIds, String openid) {
        return userDao.findByRoleOpenid(roleIds, openid);
    }

    /**
     * 修改支付密码
     *
     * @param dto
     * @return
     */
    @Override
    public boolean updatePayPwd(AuthcUser dto) {
        User u = new User();
        u.setId(dto.getId());
        u.setPayPwd(dto.getPayPwd());

        User findById = userDao.findById(u.getId());
        if (StringUtil.isBlankObject(findById)) {
            ExpUtil.throwEx("用户不存在！");
        }
        int code = EGeneralYesNo.YES.getCode();
        if (EGeneralYesNo.YES.getCode() == findById.getIsPayPwd()) {
            if (!PwdUtil.matchesPwd(dto.getOldPayPwd(), findById.getPayPwd())) {
                ExpUtil.throwEx("旧支付密码错误！");
            }
        } else {
            u.setIsPayPwd(code);
        }

        userDao.updateById(u);
        return true;
    }

    /**
     * 获取当前佣金信息
     *
     * @param id
     * @return
     */
    @Override
    public UserVo findBalance(String id) {
        return userDao.findBalance(id);
    }

    /**
     * 获取当前余额信息
     *
     * @param id
     * @return
     */
    @Override
    public UserVo findBrokerage(String id) {
        return userDao.findBrokerage(id);
    }

    /**
     * 获取下级用户列表
     *
     * @param user
     * @return
     */
    @Override
    public List<UserVo> findUnderlingByDto(UserPageDto user) {
        return userDao.findUnderlingByDto(user);
    }

    /**
     * 获取简历
     *
     * @param id
     * @return
     */
    @Override
    public UserVo findResume(String id) {
        return userDao.findResume(id);
    }

    /**
     * 获取简历
     *
     * @param user
     * @return
     */
    @Override
    public List<UserVo> findResumeList(UserPageDto user) {
        return userDao.findResumeList(user);
    }

    /**
     * 将佣金转入余额
     *
     * @param balanceLog
     * @return
     * @throws IOException
     */
    @Override
    public boolean balanceToBrokerage(BigDecimal total) throws IOException {
        String userId = TokenUtil.getUserId();
        synchronized (Lock.BALANCE_LOG) {
            userService.balanceToBrokerageGo(total, userId);
        }
        return true;
    }

    @Transactional(rollbackFor = Throwable.class)
    public int balanceToBrokerageGo(BigDecimal total, String userId) throws IOException {
        User findById = userDao.findById(userId);
        BigDecimal balance = findById.getBalance();
        if (balance.doubleValue() < total.doubleValue()) {
            ExpUtil.throwEx("佣金余额不足！");
        }

        // 修改
        User user = new User();
        user.setId(userId);
        user.setBalance(balance.subtract(total));
        user.setBalanceWithdrawBrokerage(findById.getBalanceWithdrawBrokerage().add(total));
        user.setBrokerage(findById.getBrokerage().add(total));
        user.setBrokerageSum(findById.getBrokerageSum().add(total));
        int updatePartialById = userDao.updateById(user);
        if (updatePartialById < 1) {
            ExpUtil.throwEx("修改用户余额失败！");
        }

        return updatePartialById;
    }

    /**
     * 获取对应角色的用户列表
     *
     * @param dto
     * @return
     */
    @Override
    public List<UserVo> listByRole(UserPageDto dto) {
        return userDao.listByRole(dto);
    }

    /**
     * 根据对象查询
     *
     * @param user
     * @return
     */
    @Override
    public List<UserVo> listUser(UserVo user) {
        return userDao.listUser(user);
    }

    /**
     * 获取下拉列表
     *
     * @param dto
     * @return
     */
    @Override
    public List<Select> select(UserPageDto dto) {
        return userDao.select(dto);
    }

    /**
     * 获取商家下拉列表
     *
     * @param dto
     * @return
     */
    @Override
    public List<Select> selectMerchant(UserPageDto dto) {
        return userDao.selectMerchant(dto);
    }

    /**
     * 用户续租
     *
     * @param dto
     * @return
     * @throws IOException
     */
    @Override
    public boolean addDayGoOn(UserDto dto) {
        synchronized (Lock.ADD_DAY_GO_ON) {
            UserVo findById = userDao.findById(dto.getId());
            if (StringUtil.isBlankObject(findById)) {
                ExpUtil.throwEx("用户不存在！");
            }
            if (findById.getDisableAtType() != EDisableAtType.TIME_LIMIT.getCode()) {
                ExpUtil.throwEx("用户有效类型不是限时！");
            }

            long time = findById.getDisableAt().getTime();
            long nowLong = DateUtil.nowLong();
            if (time < nowLong) {
                time = nowLong;
            }

            // 参数初始化
            User user = new User();
            user.setId(dto.getId());
            user.setDisableAt(new Date(time + Constant.MILLIS_DAY * dto.getDayGoOn()));
            userDao.updateById(user);
            return true;
        }
    }

    /**
     * 审核认证信息
     *
     * @param user
     * @return
     */
    @Override
    public boolean auditAuth(User user) {
        userDao.updateById(user);
        return true;
    }

}